﻿
import PathNode from "./PathNode";
import { Vec3 } from "cc";

export default class AStarWrapper
{
    private Size: number;
    private Width: number;
    private Height: number;
    private GoMap: any = null;

 
    private Map: any = null;
    private OpenList: Array<PathNode> = null;
    private CloseList: Array<PathNode> = null;
    private FoundPath: Array<PathNode> = null;
    private AfterFilteFoundPath: Array<PathNode> = null;
    private StartGridPointer: PathNode = null;
    private targetGridPointer: PathNode = null;

    // 斜角移动花费
    public DdCost: number = 14.14;

    //直线移动花费
    public DsCost: number = 10;


    public GetFoundPath():Array<PathNode>  {
        return this.FoundPath;
    }

    public GetFilterFoundPath(): Array<PathNode> {
        return this.AfterFilteFoundPath;
    }

    public GetFilterRoadData(): Array<any> {
        if (this.AfterFilteFoundPath === null) {
            return null;
        }
        
        var ret = new Array<any>();
        for (var i = 0; i < this.AfterFilteFoundPath.length; i++) {
            var vec = { x: this.AfterFilteFoundPath[i].indexI * this.Size, 
                        y: 0, 
                        z: this.AfterFilteFoundPath[i].indexJ * this.Size }; 
            ret.push(vec);
        }

        console.log( this.FoundPath);
        return ret;
    }

    public GetRoadData(): Array<any>
    {
        if (this.FoundPath === null) {
            return null;
        }

        var ret = new Array<any>();
        for (var i = 0; i < this.FoundPath.length; i++) {
            var vec = { x: this.FoundPath[i].indexI * this.Size, 
                        y: 0, 
                        z: this.FoundPath[i].indexJ * this.Size }; 
            ret.push(vec);
        }

        return ret;
    }


    //初始化
    public InitWithMapData(GoMap: any, width: number, height: number, Size: number): void
    {
        this.Size = Size;
        this.GoMap = GoMap;
        
        //创建开表，闭表
        this.OpenList = new Array<PathNode>();
        this.CloseList = new Array<PathNode>();
        this.AfterFilteFoundPath = new Array<PathNode>();
        //StartGridPointer = null;
        //从Tile里面获取路径信息
        this.GreateMapFromTile(GoMap, width, height);
    }

    private GreateMapFromTile(GoMap: any, width: number, height: number): void {
        this.Width = width;
        this.Height = height;

        this.Map = new Array<PathNode>();
        for (var j = 0; j < this.Height; j++) {
            var line = [];
            for (var i = 0; i < this.Width; i++) {
                //读取信息创建
                line.push(this.CreateGridFromTile(i, j));
            }
            this.Map.push(line);
        }
    }

    private CreateGridFromTile(i: number, j: number): PathNode {
        var p: PathNode = new PathNode();
        p.Traversable = this.GoMap[j][i];
        p.indexI = i;
        p.indexJ = j;
        return p;
    }


    //将世界空间的坐标信息转换到我这个图中的坐标
    private WorldPositionTo(WorldPosition: Vec3, out: any): void 
    {
        out.i = Math.round(Math.abs(WorldPosition.x) / this.Size);
        out.j = Math.round(Math.abs(WorldPosition.z) / this.Size);
    }
    
    public AstarSearch(startPosition: Vec3, endPosition: Vec3): boolean {
        var Si, Sj, Ei, Ej;
        var out: any = {};

        this.WorldPositionTo(startPosition, out);
        Si = out.i; 
        Sj = out.j;
        
        this.WorldPositionTo(endPosition, out);
        Ei = out.i;
        Ej = out.j;
        
        console.log(Si + ":" + Sj);
        console.log(Ei + ":" + Ej);

        if (this.GoMap[Ej][Ei]){
            this.StartGridPointer = this.Map[Sj][Si];
            this.targetGridPointer = this.Map[Ej][Ei];

            this.AstarMapPathFind();
            return true;
        }

        return false;
    }

    private AstarMapPathFind(): boolean
    {
        //定义一个开表，其为需要评估的点
        //定义一个闭表，其为已经评估完了的点
        //将起始点加入到开表中
        this.ClearLastFindMemory();
        this.OpenList.push(this.StartGridPointer);
        //循环，直到开表中没有节点
        while (this.OpenList.length > 0)
        {
            // 选择一个 当前点 当前点是开表中需要评估的点中，评估出来最接近目标点的点
            var currentNode: PathNode = this.FindMinimalCostInOpenList();
            //将当前点从开表中移除并加入到闭表中
            var index = this.OpenList.indexOf(currentNode);
            this.OpenList.splice(index, 1);
            this.CloseList.push(currentNode);
            //如果我们当前点就是目标点，那么结束寻路
            if (currentNode === this.targetGridPointer)
            {
                this.RetracePath(this.StartGridPointer, this.targetGridPointer);
                return true;
            }
            else
            {
                //查找当前点的所有邻接点
                var Neiborgh: Array<PathNode> = this.GetNeighbour(currentNode);
                for (var i = 0; i < Neiborgh.length; i++)
                {
                    //如果某个邻接点已经在闭表中（即评估过）或者是不可通过的则跳过这个邻接点
                    if ((this.CloseList.indexOf(Neiborgh[i]) >= 0) || Neiborgh[i].Traversable === false)
                        continue;
                    var newGCost = this.GetDistance(currentNode, Neiborgh[i])+currentNode.Gcost;
                    //如果从当前点到到其邻接点更短或邻接点不在开表中
                    if (newGCost < Neiborgh[i].Gcost || (this.OpenList.indexOf(Neiborgh[i]) < 0))
                    {
                        //那么计算邻接点的评估值
                        Neiborgh[i].Gcost = newGCost;
                        Neiborgh[i].Hcost = this.GetDistance(Neiborgh[i], this.targetGridPointer);
                        Neiborgh[i].TotalCost = Neiborgh[i].Gcost + Neiborgh[i].Hcost;
                        //将其上一节点设置成当前节点
                        Neiborgh[i].LastStepGrid = currentNode;
                        //如果邻接点不在开表中，就把他加如开表中
                        if ((this.OpenList.indexOf(Neiborgh[i]) < 0))
                            this.OpenList.push(Neiborgh[i]);
                    }
                }
            }
        }
        
        return false;
    }

    private GetDistance(curGrid: PathNode, tarGrid: PathNode): number
    {
        //H对角 = min(X方向差值, Y方向差值)
        //斜角行走消耗
        var H_diagonal: number = Math.min(Math.abs(curGrid.indexI - tarGrid.indexI), Math.abs(curGrid.indexJ - tarGrid.indexJ));
        //直线行走消耗
        //H直线  = X方向差值 + Y方向差值
        var H_straight = Math.abs(curGrid.indexI - tarGrid.indexI) + Math.abs(curGrid.indexJ - tarGrid.indexJ);
        //H(n) = Dd*H对角 + Ds*(H直线-2H对角)
        var HCost = this.DdCost * H_diagonal + this.DsCost * (H_straight - 2 * H_diagonal);
        return HCost;
    }

    private GetNeighbour(inGrid: PathNode): Array<PathNode>
    {
        var Neighbours: Array<PathNode> = new Array<PathNode>();
        for (var x = -1; x <= 1; x++)
        {
            for (var y = -1; y <= 1; y++)
            {
                if (x === 0 && y === 0)
                    continue;

                var checkX = inGrid.indexI + x;
                var checkY = inGrid.indexJ + y;

                if (checkX >= 0 && checkX < this.Width && checkY >= 0 && checkY < this.Height)
                {
                    Neighbours.push(this.Map[checkY][checkX]);
                }
            }
        }
        return Neighbours;
    }

    private FindMinimalCostInOpenList(): PathNode {
        var path_Grid: PathNode = this.OpenList[0];
        for (var i = 1; i < this.OpenList.length; i++)
        {
            if (path_Grid.TotalCost >= this.OpenList[i].TotalCost)
            {
                path_Grid = this.OpenList[i];
            }
        }
        return path_Grid;
    }

    private CheckPointAllPass(i: number, j: number): boolean
    {
        if (i + 1 === this.Width || !this.GoMap[j][i + 1])
            return false;

        if (i === 0 || !this.GoMap[j][i - 1])
            return false;


        if (j + 1 === this.Height || !this.GoMap[j + 1][i])
            return false;

        if (j === 0 || !this.GoMap[j - 1][i])
            return false;


        if (i + 1 === this.Width || j + 1 === this.Height || !this.GoMap[j + 1][i + 1])
        {
            return false;
        }

        if (i === 0 || j === 0 || !this.GoMap[j - 1][i - 1])
        {
            return false;
        }

        if (i === 0 || j + 1 === this.Height || !this.GoMap[j + 1][i - 1])
        {
            return false;
        }

        if (i === this.Width || j === 0 || !this.GoMap[j - 1][i + 1])
        {
            return false;
        }

        return true;
    }

    private FilterPath(): void
    {
        this.AfterFilteFoundPath = [];
        this.AfterFilteFoundPath.push(this.FoundPath[0]);

        var i = 1;
        for (i = 1; i < this.FoundPath.length - 1; i++) {
            if (this.CheckPointAllPass(this.FoundPath[i].indexI, this.FoundPath[i].indexJ)) {
                continue;
            }
            else {
                this.AfterFilteFoundPath.push(this.FoundPath[i]);
            }
        }

        this.AfterFilteFoundPath.push(this.FoundPath[i]);
    }

    private RetracePath(startNode: PathNode , endNode: PathNode):void 
    {
        var path: Array<PathNode> = new Array<PathNode>();
        var currentNode: PathNode = endNode;

        while (currentNode !== startNode)
        {
            path.push(currentNode);
            currentNode = currentNode.LastStepGrid;
        }
        path.push(this.StartGridPointer);
        path.reverse();
        this.FoundPath = path;

        this.FilterPath();
    }
    
    //清除上一次寻路对当前网格的影响
    private ClearLastFindMemory(): void
    {
        for (var i = 0; i < this.Height; i++)
        {
            for (var j = 0; j < this.Width; j++)
            {
                this.Map[i][j].Gcost = 0;
                this.Map[i][j].Hcost = 0;
                this.Map[i][j].TotalCost = 0;
                this.Map[i][j].LastStepGrid = null;
            }
        }
        this.OpenList = [];
        this.CloseList = [];
    }
}
